package net.pws.common.persistence.hibernate3;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.query.HQLQueryPlan;
import org.hibernate.engine.query.ReturnMetadata;
import org.hibernate.hql.classic.ParserHelper;
import org.hibernate.impl.SessionFactoryImpl;
import org.hibernate.proxy.HibernateProxy;

public abstract class HibernateUtils {
    
    public static void tryInitializeProxy(Object proxyObj) {
        if (proxyObj instanceof HibernateProxy) {
            Hibernate.initialize(proxyObj);
        }
    }
    
    /**
     * @param sessionFactory
     * @param queryStatement
     *            查询语句
     * @param in
     *            如果成员为数组，则转化为Map，需要查询语句的元数据
     * @return
     */
    public static List convertArrayListToMapList(Session session,
                                                 String queryStatement,
                                                 List in) {
        return convertArrayListToMapList(session.getSessionFactory(),
                                         queryStatement,
                                         in);
    }
    
    /**
     * @param sessionFactory
     * @param queryStatement
     *            查询语句
     * @param in
     *            如果成员为数组，则转化为Map，需要查询语句的元数据
     * @return
     */
    public static List convertArrayListToMapList(SessionFactory sessionFactory,
                                                 String queryStatement,
                                                 List in) {
        if (!(sessionFactory instanceof SessionFactoryImpl)) {
            return in;
        }
        SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) sessionFactory;
        HQLQueryPlan plan = sessionFactoryImpl.getQueryPlanCache()
                                              .getHQLQueryPlan(queryStatement,
                                                               false,
                                                               Collections.EMPTY_MAP);
        
        ReturnMetadata returnMetadata = plan.getReturnMetadata();
        List result = new ArrayList();
        for (Iterator iterator = in.iterator(); iterator.hasNext();) {
            Object object = (Object) iterator.next();
            String[] returnAliases = returnMetadata.getReturnAliases();
            
            // if the element of the list is not an array instance,convert it to
            // array-typed object
            Object[] objects;
            int length;
            if (!object.getClass().isArray()) {
                objects = new Object[] { object };
                length = 1;
            }
            else {
                objects = (Object[]) object;
                length = returnAliases.length;
            }
            
            if ((length != 1) && (returnAliases.length != objects.length)) {
                throw new IllegalArgumentException("The input list[" + in
                                                   + "] is not matched for query["
                                                   + queryStatement
                                                   + "]");
            }
            
            Map item = new HashMap();
            for (int i = 0; i < length; i++) {
                item.put(returnAliases[i], objects[i]);
            }
            result.add(item);
        }
        
        return result;
    }
    
    /**
     * 直接对HQL进行parse,并过滤掉select子句和order by子句,支持子查询
     */
    public static String eraseSelectAndOrderByClause(String hql4Query) {
        StringTokenizer tokens = new StringTokenizer(hql4Query,
                                                     ParserHelper.HQL_SEPARATORS,
                                                     true);
        int parenthesesDepth = 0;
        int singleQuoteDepth = 0;
        int doubleQuotesDepth = 0;
        int fromPosition = 0;
        int orderPosition = 0;
        boolean orderFound = false;
        boolean fromFound = false;
        boolean orderByFound = false;
        StringBuffer temp = new StringBuffer(20);
        
        while (tokens.hasMoreTokens()) {
            String token = tokens.nextToken();
            // () '' "" 内部包含的子查询或者关键字将被忽略.
            if ("(".equals(token)) {
                parenthesesDepth++;
            }
            else if (")".equals(token)) {
                if (parenthesesDepth == 0) {
                    throw new IllegalArgumentException(") meeted but no ( existed before!");
                }
                parenthesesDepth--;
            }
            else if ("'".equals(token)) {
                if (singleQuoteDepth == 0) {
                    singleQuoteDepth++;
                }
                else {
                    singleQuoteDepth--;
                }
            }
            else if ("\"".equals(token)) {
                if (doubleQuotesDepth == 0) {
                    doubleQuotesDepth++;
                }
                else {
                    doubleQuotesDepth--;
                }
            }
            else if ("from".equalsIgnoreCase(token)) {
                if (!fromFound && parenthesesDepth == 0
                    && singleQuoteDepth == 0
                    && doubleQuotesDepth == 0) {
                    fromFound = true;
                    fromPosition = temp.length();
                }
            }
            else if ("order".equalsIgnoreCase(token)) {
                if (parenthesesDepth == 0 && singleQuoteDepth == 0
                    && doubleQuotesDepth == 0) {
                    orderFound = true;
                    orderPosition = temp.length();
                }
            }
            else if (" ".equals(token) || "\n".equals(token)
                     || "\r".equals(token)
                     || "\t".equals(token)
                     || "\f".equals(token)) {
                // notice:
                // exclude this from the last else branch,
                // because " \n\r\t\f" is valid char between order and by
            }
            else if ("by".equalsIgnoreCase(token)) {
                if (orderFound) {
                    orderByFound = true;
                    break;
                }
            }
            else {
                if (orderFound) {
                    orderFound = false;
                }
            }
            
            temp.append(token);
        }
        
        if (orderByFound) {
            return hql4Query.substring(fromPosition, orderPosition);
        }
        else {
            return hql4Query.substring(fromPosition);
        }
    }
    
}
